#include "StdAfx.h"

#include "..\INC\UsbHid.h"

#include "..\INC\hidsdi++.h"
#include "..\INC\hid.h"
#include "..\INC\setupapi.h"
#include "..\INC\usb100.h"
#include <dbt.h>

#ifndef	LIB_USB_DEFINE
#pragma comment(lib, "..\\USB\\LIB\\setupapi.lib")
#pragma comment(lib, "..\\USB\\LIB\\hid.lib")
#endif

xstring		toXString(LPCWSTR str)
{
#ifdef	UNICODE
	return xstring(str);
#else
	int len = ::WideCharToMultiByte(CP_ACP,0,str,-1,NULL,0,NULL,NULL);
	char *pStr = new char[len+1];
	::WideCharToMultiByte(CP_ACP,0,str,-1,pStr,len+1,NULL,NULL);
	xstring res(pStr);
	delete [] pStr;
	return res;
#endif
}
xstring		toXString(LPCSTR str)
{
#ifdef	UNICODE
	int len = ::MultiByteToWideChar(CP_ACP,0,str,-1,NULL,0);
	wchar_t *pStr = new wchar_t[len+1];
	::MultiByteToWideChar(CP_ACP,0,str,-1,pStr,len+1);
	/// InitialDevice(pStr);
	xstring res(pStr);
	delete [] pStr;
	return res;
#else
	return xstring(str);
#endif
}


LONG	CUsbHid::Open(LPCTSTR lpDevice)
{
	m_devicePath[0] = 0;
    memset(m_deviceSN,0,sizeof(m_deviceSN));
	m_hFile = ::CreateFile(
			lpDevice,
			GENERIC_READ | GENERIC_WRITE,
			FILE_SHARE_READ | FILE_SHARE_WRITE,
			NULL, //&SecurityAttributes,  //no SECURITY_ATTRIBUTES structure
			OPEN_EXISTING,  //No special create flags
			FILE_FLAG_OVERLAPPED, // No special attributes
			NULL);
	if (m_hFile == INVALID_HANDLE_VALUE){
		m_hFile = NULL;
		return (LONG)::GetLastError();
	}
	m_hevtOverlappedWorkerThread = ::CreateEvent(0,true,false,0);
	if (m_hevtOverlappedWorkerThread == 0)
	{
		// Obtain the error information
		m_lLastError = ::GetLastError();
		Close();
		// Return the error
		return m_lLastError;
	}

	m_lLastError = StartListener();
	if (m_lLastError != ERROR_SUCCESS)
		return m_lLastError;

    if(!InitializeCriticalSectionAndSpinCount(&m_criticalSection,0x1000)){
        m_lLastError = ::GetLastError();
        return m_lLastError;
    }
	// Return the error
	HIDD_ATTRIBUTES  Attributes;
	if(GetAttributes(&Attributes)){
		m_curDeviceVer = Attributes.VersionNumber;
		if(m_curDeviceVer >= 0x200){
			wchar_t ch[128] = L"";
			if(HidD_GetIndexedString(m_hFile,4,ch,128)){
				ch[2] = 0; // 00.00.0000
				ch[5] = 0;
				int ma,mi,build;
				swscanf(ch,L"%x",&ma);
				swscanf(ch+3,L"%x",&mi);
				swscanf(ch+6,L"%x",&build);
				m_firmwareVer = (WORD)((ma<<8) + mi);
				m_firmwareBuild = (WORD)build;
			}
		}else{
			m_firmwareVer = m_curDeviceVer;
			m_firmwareBuild = 0;
		}
	}
    GetSerialString(m_deviceSN,sizeof(m_deviceSN));

	_tcscpy_s(m_devicePath,lpDevice);
	return m_lLastError;
}

LONG	CUsbHid::Open(WORD vid, WORD pid, HWND hParent, LPCTSTR filter /*= NULL*/)
{
	m_vid = vid;
	m_pid = pid;
	m_filter = filter;
	m_hParent = hParent;
	RegisterDevice();
	vector<CHidDevice>	devs;
	EnumHidDevice(vid,pid,devs,filter);
	if(devs.size()){
		_tcscpy_s(m_devicePath,256,devs[devs.size()-1].m_path.c_str());
		LONG err = Open(devs[devs.size()-1].m_path.c_str());
		if(err == ERROR_SUCCESS){
			if(hParent){
				m_bConnect = TRUE;
			}
		}
		return err;
	}
	return -1;
}

LONG	CUsbHid::Monitor(WORD vid, WORD pid, HWND hParent, LPCTSTR filter /*= NULL*/)
{
	m_vid = vid;
	m_pid = pid;
	m_filter = filter;
	m_hParent = hParent;
	return RegisterDevice();
}

LONG	CUsbHid::Open(WORD vid, WORD pid, HWND hParent, USAGE usagePage, USAGE usage, LPCTSTR filter)
{
	m_usagePage = usagePage;
	m_usage = usage;
	m_vid = vid;
	m_pid = pid;
	m_filter = filter;
	m_hParent = hParent;
	RegisterDevice();
	vector<CHidDevice>	devs;
	EnumHidDevice(vid,pid,devs,usagePage,usage);
	if(devs.size()){
		_tcscpy_s(m_devicePath,256,devs[devs.size()-1].m_path.c_str());
		LONG err = Open(devs[devs.size()-1].m_path.c_str());
		if(err == ERROR_SUCCESS){
			if(hParent){
				m_bConnect = TRUE;
			}
		}
		return err;
	}
	return -1;
}

LONG	CUsbHid::Monitor(WORD vid, WORD pid, HWND hParent, USAGE usagePage, USAGE usage, LPCTSTR filter)
{
	m_usagePage = usagePage;
	m_usage = usage;
	return Monitor(vid, pid, hParent, filter);
}

LONG	CUsbHid::Close()
{
    if(m_hFile){
		::CloseHandle(m_hFile);
		m_hFile = NULL;
        DeleteCriticalSection(&m_criticalSection);
	}
	StopListener(1);
	return ERROR_SUCCESS;
}

/// Must write 8 byte every time
LONG	CUsbHid::Write(const void* pData, size_t len, DWORD* realWrite)
{
	if(m_curDeviceVer < 0x0200) /// Less than 2.0
	{
		DWORD ttp;
		if(realWrite == NULL){ 
			realWrite = &ttp;
		}
		BYTE buf[2]={0x01,0x55};
		const BYTE* data = (const BYTE*)pData;
		DWORD wr=0;
		HANDLE hEvent = ::CreateEvent(0,true,false,0);
		OVERLAPPED	ov;
		memset(&ov,0,sizeof(OVERLAPPED));
		ov.hEvent = hEvent;
		/*
		BOOL res = ::WriteFile(m_hFile,buf,2,&wr,&ov);
		if (::WaitForSingleObject(hEvent,INFINITE) != WAIT_OBJECT_0)
		{
			// Set the internal error code
			m_lLastError = ::GetLastError();
			return m_lLastError;
		}
		*/
		for(size_t i=0;i<len;i++){
			buf[1]=*data;
			BOOL res = ::WriteFile(m_hFile,buf,2,&wr,&ov);
			if (::WaitForSingleObject(hEvent,10000) != WAIT_OBJECT_0)
			{
				// Set the internal error code
				m_lLastError = ::GetLastError();
				*realWrite = (DWORD)i;
				return m_lLastError;
			}
			data++;
		}
		*realWrite = (DWORD)len;
		return ERROR_SUCCESS;
	}else{
		/// version is higher or equal than 2.00
		HANDLE hEvent = ::CreateEvent(0,true,false,0);
		OVERLAPPED	ov;
		memset(&ov,0,sizeof(OVERLAPPED));
		ov.hEvent = hEvent;
		DWORD wr;
		BYTE buf[64] = "";
		memcpy(buf,pData,len);
		if(buf[0] == 0x55){
			buf[0] = 0x11;
		}
		BOOL res = ::WriteFile(m_hFile,buf,64,&wr,&ov);
		if (::WaitForSingleObject(hEvent,2000) != WAIT_OBJECT_0){
				// Set the internal error code
			m_lLastError = ::GetLastError();
			return m_lLastError;
		}
		return ERROR_SUCCESS;
	}

}

LONG	CUsbHid::Read(void* pData, size_t len, DWORD* realRead)
{
	DWORD ttp;
	if(realRead == NULL){
		realRead = &ttp;
	}
	const BYTE* data = (const BYTE*)pData;
	DWORD wr=0;
	HANDLE hEvent = ::CreateEvent(0,true,false,0);
	OVERLAPPED	ov;
	memset(&ov,0,sizeof(OVERLAPPED));
	ov.hEvent = hEvent;
	BOOL res = ::WriteFile(m_hFile,pData,2,realRead,&ov);
	if (::WaitForSingleObject(hEvent,INFINITE) != WAIT_OBJECT_0)
	{
		// Set the internal error code
		m_lLastError = ::GetLastError();
		return m_lLastError;
	}
	return ERROR_SUCCESS;
}

LONG	CUsbHid::StartListener()
{
	// Check if the watcher thread was already running
	if (m_hListenerThread == 0)
	{
		// Make sure the thread has stopped
		_ASSERTE(!m_fStopping);

		// Start the watcher thread
		DWORD dwThreadId = 0;
		m_hListenerThread = ::CreateThread(0,0,ThreadProc,LPVOID(this),0,&dwThreadId);
		if (m_hListenerThread == 0)
		{
			// Set the error code and exit
			m_lLastError = ::GetLastError();
			return m_lLastError;
		}
	}
	// Return the error
	m_lLastError = ERROR_SUCCESS;
	return m_lLastError;
}

LONG	CUsbHid::StopListener(DWORD dwTimeout)
{
	// Check if the thread is running
	if (m_hListenerThread)
	{
		DWORD code;
		::GetExitCodeThread(m_hListenerThread,&code);
		if(code == STILL_ACTIVE){
			// Set the flag that the thread must be stopped
			m_fStopping = true;
			::SetEvent(m_hevtOverlappedWorkerThread);
			::CancelIo(m_hFile);
			DWORD err = ::GetLastError();
			if(::WaitForSingleObject(m_hListenerThread,dwTimeout) != WAIT_OBJECT_0){
				::TerminateThread(m_hListenerThread,0);
			}

			// The thread has stopped
			m_fStopping = false;
		}

		// Close handle to the thread
		::CloseHandle(m_hListenerThread);
		m_hListenerThread = NULL;
	}
	// Return the error
	m_lLastError = ERROR_SUCCESS;
	return m_lLastError;
}

DWORD WINAPI CUsbHid::ThreadProc (LPVOID lpArg)
{
	return ((CUsbHid*)lpArg)->ThreadProc();
}

BOOL	CUsbHid::ReadUntil(void* pData, DWORD len, DWORD dwTimeout)
{
	return TRUE;
}

DWORD	CUsbHid::ThreadProc (void)
{
	OVERLAPPED	ov;
	memset(&ov,0,sizeof(OVERLAPPED));
	//LPOVERLAPPED lpOverlapped = 0;
	// Keep looping
	do
	{
		DWORD read;
		memset(&ov,0,sizeof(OVERLAPPED));
		ov.hEvent = m_hevtOverlappedWorkerThread;
		BYTE	data[256]={0};
		::ReadFile(m_hFile,data,65,&read,&ov);
		if ((::WaitForSingleObject(m_hevtOverlappedWorkerThread,INFINITE) != WAIT_OBJECT_0))
		{
			// Set the internal error code
			m_lLastError = ::GetLastError();
			return m_lLastError;
		}
		if(m_fStopping){
			break;
		}
		if(ov.InternalHigh > 0){
            EnterCriticalSection(&m_criticalSection);
			OnHidData(data+1,ov.InternalHigh-1);
            LeaveCriticalSection(&m_criticalSection);
		}else{
			break;
		}

		//if(data[0] == 0x07){
		//	printf("0x%X\n",data[1]);
		//}
	}
	while (!m_fStopping);
	if(!m_fStopping){
		//OnDisconnect(m_devicePath);
	}
	// Bye bye
	return 0;
}

#define	IOCTL_BULKUSB_GET_DEVICE_SN			CTL_CODE(FILE_DEVICE_UNKNOWN,     \
                                                     3, \
                                                     METHOD_BUFFERED,         \
                                                     FILE_ANY_ACCESS)
const GUID bulkGuid = {
     0x7243a85, 0xe5a0, 0x4cec, {0x8a, 0x32, 0xeb, 0x3a, 0x1b, 0x30, 0xad, 0xd8}};
LRESULT	CUsbHid::OnDeviceChange(WPARAM wParam, LPARAM lParam)
{
	TCHAR vid_str[64];
	TCHAR pid_str[64];
	_stprintf_s(vid_str,64,_T("VID_%04X"),m_vid);
	_stprintf_s(pid_str,64,_T("PID_%04X"),m_pid);
	PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam;
	PDEV_BROADCAST_DEVICEINTERFACE pDevInf;
	if(DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam){
		if( pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE){
			pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
            GUID guidHID;
		    HidD_GetHidGuid(&guidHID);
            if(IsEqualGUID(pDevInf->dbcc_classguid,guidHID)){
			    LPTSTR p = pDevInf->dbcc_name;
			    while(*p){
				    if(::islower(*p)){*p -= (_T('a')-_T('A'));}
				    p++;
			    }
			    if(_tcsstr(pDevInf->dbcc_name, vid_str) != NULL
				    && ( (m_pid == 0) || (_tcsstr(pDevInf->dbcc_name, pid_str) != NULL) )
				    && ( (m_filter == NULL) || (_tcsstr(pDevInf->dbcc_name, m_filter) != NULL) )
				    )
			    {
					    if(DBT_DEVICEARRIVAL == wParam){
						    if(CheckUsage(xstring(pDevInf->dbcc_name),m_usagePage,m_usage)){
							    OnConnect(pDevInf->dbcc_name);
						    }
					    }else{
						    OnDisconnect(pDevInf->dbcc_name);
					    }
			    }
            }else if(IsEqualGUID(pDevInf->dbcc_classguid,bulkGuid)){
                if(DBT_DEVICEARRIVAL == wParam){
                    BOOL bFind = FALSE;
                    HANDLE hFile = ::CreateFile(
                        pDevInf->dbcc_name,
                        GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL, // no SECURITY_ATTRIBUTES structure
                        OPEN_EXISTING, // No special create flags
                        0, // No special attributes
                        NULL); // No template file
                    if(hFile != INVALID_HANDLE_VALUE){
                        wchar_t sn[32] = L"";
                        DWORD nBytes;
                        if(DeviceIoControl(hFile,IOCTL_BULKUSB_GET_DEVICE_SN,
                            sn,32,sn,32,&nBytes,NULL)){
                                if(wcscmp(m_deviceSN,sn) == 0){
                                    bFind = TRUE;
                                }
                        }
                    }
                    ::CloseHandle(hFile);
                    if(bFind){
                        OnBulkConnect(pDevInf->dbcc_name);
                    }
                }else{
                    OnBulkDisconnect(pDevInf->dbcc_name);
                }
            }
		}
	}
	return 0;
}
LRESULT	CUsbHid::RegisterDevice()
{
	if(m_hParent){
		HDEVNOTIFY hDevNotify;
		DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
		ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
		NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
		NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
		GUID Guid;
		HidD_GetHidGuid(&Guid);
		NotificationFilter.dbcc_classguid = Guid; 
		hDevNotify = RegisterDeviceNotification(m_hParent, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
		if( !hDevNotify ) {
			::MessageBox(NULL,_T("Can't register device notification: "),_T("Error!"),MB_OK);
		}
        NotificationFilter.dbcc_classguid = bulkGuid; 
        hDevNotify = RegisterDeviceNotification(m_hParent, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
        if( !hDevNotify ) {
			::MessageBox(NULL,_T("Can't register bulk device notification: "),_T("Error!"),MB_OK);
		}
	}
	return ERROR_SUCCESS;
}

void	CheckFileMap(WORD vid, WORD pid, vector<CHidDevice>& devs)
{
	TCHAR fileName[MAX_PATH];
	_stprintf_s(fileName,64,_T("USBFileMapvid_%04xpid_%04x"),vid,pid);
	HANDLE hMapFile = ::OpenFileMapping(FILE_MAP_ALL_ACCESS,FALSE,fileName);
	DWORD err = ::GetLastError();
	if (hMapFile == NULL || hMapFile == INVALID_HANDLE_VALUE) {return;}
	LPCSTR	pBuf = (LPCSTR)MapViewOfFile(hMapFile,    // handle to mapping object
               FILE_MAP_ALL_ACCESS,  // read/write permission
               0,                    
               0,                    
               MAX_PATH);
	if(pBuf==NULL){
		CloseHandle(hMapFile);
		return;
	}
	CHidDevice dev;
	dev.m_path = toXString(pBuf);
	dev.m_desc = _T("USB File Map");
	devs.push_back(dev);

	UnmapViewOfFile(pBuf);
	CloseHandle(hMapFile);
}


BOOL	CUsbHid::GetAttributes(PHIDD_ATTRIBUTES  Attributes)
{
	if(m_hFile){
		return  HidD_GetAttributes(m_hFile,Attributes);
	}
	return FALSE;
}

BOOL	CUsbHid::GetSerialString(void * p, ULONG len)
{
	if(m_hFile){
		return  HidD_GetSerialNumberString(m_hFile,p,len);
	}
	return FALSE;
}

LONG	CUsbHid::IoControl(void* pData, size_t len, DWORD *realRead)
{
	DWORD read;
	if(realRead == NULL){
		realRead = &read;
	}
	DWORD code = CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS);
	BOOL res = DeviceIoControl(
		this->m_hFile,
		code,
		pData,
		(DWORD)len,
		NULL,
		0,
		realRead,
		NULL
		);
	if(res){
		m_lLastError = ERROR_SUCCESS;
	}else{
		m_lLastError = GetLastError();
	}
	return m_lLastError;
}

LONG	CUsbHid::SetFeature(void* pData, size_t len, DWORD *realRead)
{
	BOOL res = HidD_SetFeature(m_hFile,pData,(ULONG)len);
	return GetLastError();
}

BOOL	CUsbHid::GetDeviceSerial(CDeviceSerial& serial)
{
	if(m_hFile){
		wchar_t ch[128];
		BOOL res = HidD_GetIndexedString(m_hFile,3,ch,128);
		memcpy(&serial,ch,30);
		serial.serial[12] = 0;
		return res;
	}
	return FALSE;
}


BOOL	CUsbHid::GetFirmwareInfo(LPWSTR version)
{
	if(m_hFile){
		wchar_t ch[128];
		BOOL res = HidD_GetIndexedString(m_hFile,4,ch,128);
		memcpy(version,ch,20);
		version[10] = 0;
		return res;
	}
	return FALSE;
}

BOOL    SearchGuidBySN(const LPGUID  pGuid, CHidDevice& dev,const wchar_t* SN)
{
    vector<CHidDevice> devs;
    EnumGuidDevice(pGuid,devs);
    if(!SN ){ return FALSE; }
    if(wcslen(SN) != 12){ return FALSE; }
    if(devs.size()){
        for(size_t i=0;i<devs.size();i++){
            HANDLE hFile = ::CreateFile(
                    devs[i].m_path.c_str(),
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL, // no SECURITY_ATTRIBUTES structure
                    OPEN_EXISTING, // No special create flags
                    0, // No special attributes
                    NULL); // No template file
            if(hFile != INVALID_HANDLE_VALUE){
                wchar_t sn[32] = L"";
                DWORD nBytes;
                if(DeviceIoControl(hFile,IOCTL_BULKUSB_GET_DEVICE_SN,
                    sn,32,sn,32,&nBytes,NULL)){
                        if(wcscmp(SN,sn) == 0){
                            ::CloseHandle(hFile);
                            dev = devs[i];
                            return TRUE;
                        }
                }
                ::CloseHandle(hFile);
            }
        }
    }
    return FALSE;
}
size_t  EnumGuidDevice(LPGUID  pGuid, vector<CHidDevice>& devs)
{
    devs.clear();
    CHidDevice	temp;
    TCHAR	Product[256] = _T("");
    HDEVINFO info
    = SetupDiGetClassDevs(pGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
    if(info != INVALID_HANDLE_VALUE){
		DWORD devIndex = 0;
		SP_INTERFACE_DEVICE_DATA ifData;
		ifData.cbSize=sizeof(ifData);
		for (devIndex=0;SetupDiEnumDeviceInterfaces(info, NULL, pGuid, devIndex, &ifData);++devIndex)
		{
			DWORD needed;

			SetupDiGetDeviceInterfaceDetail(info, &ifData, NULL, 0, &needed, NULL);

			PSP_INTERFACE_DEVICE_DETAIL_DATA detail=(PSP_INTERFACE_DEVICE_DETAIL_DATA)new BYTE[needed];
			detail->cbSize=sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
			SP_DEVINFO_DATA did={sizeof(SP_DEVINFO_DATA)};
			if (SetupDiGetDeviceInterfaceDetail(info, &ifData, detail, needed, NULL, &did))
			{
				// Add the link to the list of all DFU devices
				//if(    ( (vid == 0) || (_tcsstr(detail->DevicePath, vid_str) != NULL) )
				//	&& ( (pid == 0) || (_tcsstr(detail->DevicePath, pid_str) != NULL) )
				//	&& ( (lpFilter == NULL) || (_tcsstr(detail->DevicePath, lpFilter) != NULL) )
				//	)
				//{
					temp.m_path = detail->DevicePath;
					if (SetupDiGetDeviceRegistryProperty(info, &did, SPDRP_DEVICEDESC, NULL, (PBYTE)Product, 253, NULL))
						temp.m_desc = Product;
					else
						temp.m_desc = _T("(Unnamed HID device)");
				//}
			}

			//if(strstr(detail->DevicePath, vid_str) != NULL
			//	&& ( (pid == 0) || (strstr(detail->DevicePath, pid_str) != NULL) )
			//	)
			//{
			//}

			delete[] (PBYTE)detail;
			if(temp.m_desc != _T("")){
				devs.push_back(temp);
			}
		}
        SetupDiDestroyDeviceInfoList(info);
    }
    return devs.size();
}

size_t	EnumHidDevice(WORD vid, WORD pid, vector<CHidDevice>& devs,LPCTSTR lpFilter /*= NULL*/)
{
	devs.clear();
	CheckFileMap(vid,pid,devs);
	TCHAR	Product[253];
	string	Prod, String;
	GUID	Guid;
	CHidDevice	temp;
	temp.m_desc = _T("");
	TCHAR vid_str[64];
	TCHAR pid_str[64];

	_stprintf_s(vid_str,64,_T("vid_%04x"),vid);
	_stprintf_s(pid_str,64,_T("pid_%04x"),pid);

	HidD_GetHidGuid(&Guid);

	HDEVINFO info;
    info=SetupDiGetClassDevs(&Guid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
    if (info!=INVALID_HANDLE_VALUE)  
	{
		DWORD devIndex = 0;
		SP_INTERFACE_DEVICE_DATA ifData;
		ifData.cbSize=sizeof(ifData);
		//SetupDiEnumDeviceInterfaces(info, NULL, &Guid, devIndex, &ifData);
		//devIndex = ::GetLastError();
		for (devIndex=0;SetupDiEnumDeviceInterfaces(info, NULL, &Guid, devIndex, &ifData);++devIndex)
		{
			DWORD needed;

			SetupDiGetDeviceInterfaceDetail(info, &ifData, NULL, 0, &needed, NULL);

			PSP_INTERFACE_DEVICE_DETAIL_DATA detail=(PSP_INTERFACE_DEVICE_DETAIL_DATA)new BYTE[needed];
			detail->cbSize=sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
			SP_DEVINFO_DATA did={sizeof(SP_DEVINFO_DATA)};
			if (SetupDiGetDeviceInterfaceDetail(info, &ifData, detail, needed, NULL, &did))
			{
				// Add the link to the list of all DFU devices
				if(    ( (vid == 0) || (_tcsstr(detail->DevicePath, vid_str) != NULL) )
					&& ( (pid == 0) || (_tcsstr(detail->DevicePath, pid_str) != NULL) )
					&& ( (lpFilter == NULL) || (_tcsstr(detail->DevicePath, lpFilter) != NULL) )
					)
				{
					temp.m_path = detail->DevicePath;
					if (SetupDiGetDeviceRegistryProperty(info, &did, SPDRP_DEVICEDESC, NULL, (PBYTE)Product, 253, NULL))
						temp.m_desc = Product;
					else
						temp.m_desc = _T("(Unnamed HID device)");
				}
			}

			//if(strstr(detail->DevicePath, vid_str) != NULL
			//	&& ( (pid == 0) || (strstr(detail->DevicePath, pid_str) != NULL) )
			//	)
			//{
			//}

			delete[] (PBYTE)detail;
			if(temp.m_desc != _T("")){
				devs.push_back(temp);
			}
		}
		SetupDiDestroyDeviceInfoList(info);
	}

	return devs.size();
}
BOOL	CheckUsage(xstring &path, USAGE usagePage, USAGE usage)
{
	if(usagePage == 0  && usage == 0 ){
		return TRUE;
	}
	HANDLE file = CreateFile ( path.c_str(),
                            GENERIC_READ | GENERIC_WRITE,
                            FILE_SHARE_READ | FILE_SHARE_WRITE,
                            NULL, // no SECURITY_ATTRIBUTES structure
                            OPEN_EXISTING, // No special create flags
                            0, // No special attributes
                            NULL); // No template file
	if(file == INVALID_HANDLE_VALUE ){
		return FALSE;
	}
	BOOL res = TRUE;
	PHIDP_PREPARSED_DATA			Ppd; // The opaque parser info describing this device
    HIDP_CAPS                       Caps; // The Capabilities of this hid device.
	if (!HidD_GetPreparsedData (file, &Ppd))
    {
        res = FALSE;
    }
    if (!HidP_GetCaps (Ppd, &Caps))
    {
        HidD_FreePreparsedData (Ppd);
        res = FALSE;
	}else{
		if(usagePage){
			if(Caps.UsagePage != usagePage){
				res = FALSE;
			}
		}
		if(usage){
			if(Caps.Usage != usage){
				res = FALSE;
			}
		}
	}
	HidD_FreePreparsedData (Ppd);
	::CloseHandle(file);
	return res;
}
size_t	EnumHidDevice(WORD vid, WORD pid, vector<CHidDevice>& devs, USAGE usagePage, USAGE usage)
{
	devs.clear();
	CheckFileMap(vid,pid,devs);
	TCHAR	Product[253];
	string	Prod, String;
	GUID	Guid;
	CHidDevice	temp;
	temp.m_desc = _T("");
	TCHAR vid_str[64];
	TCHAR pid_str[64];

	_stprintf_s(vid_str,64,_T("vid_%04x"),vid);
	_stprintf_s(pid_str,64,_T("pid_%04x"),pid);

	HidD_GetHidGuid(&Guid);

	HDEVINFO info;
    info=SetupDiGetClassDevs(&Guid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
    if (info!=INVALID_HANDLE_VALUE)  
	{
		DWORD devIndex = 0;
		SP_INTERFACE_DEVICE_DATA ifData;
		ifData.cbSize=sizeof(ifData);
		//SetupDiEnumDeviceInterfaces(info, NULL, &Guid, devIndex, &ifData);
		//devIndex = ::GetLastError();
		for (devIndex=0;SetupDiEnumDeviceInterfaces(info, NULL, &Guid, devIndex, &ifData);++devIndex)
		{
			DWORD needed;

			SetupDiGetDeviceInterfaceDetail(info, &ifData, NULL, 0, &needed, NULL);

			PSP_INTERFACE_DEVICE_DETAIL_DATA detail=(PSP_INTERFACE_DEVICE_DETAIL_DATA)new BYTE[needed];
			detail->cbSize=sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
			SP_DEVINFO_DATA did={sizeof(SP_DEVINFO_DATA)};
			temp.m_desc = _T("");
			if (SetupDiGetDeviceInterfaceDetail(info, &ifData, detail, needed, NULL, &did))
			{
				// Add the link to the list of all DFU devices
				if(    ( (vid == 0) || (_tcsstr(detail->DevicePath, vid_str) != NULL) )
					&& ( (pid == 0) || (_tcsstr(detail->DevicePath, pid_str) != NULL) )
					)
				{
					
					temp.m_path = detail->DevicePath;
					if(CheckUsage(temp.m_path,usagePage,usage)){
						if (SetupDiGetDeviceRegistryProperty(info, &did, SPDRP_DEVICEDESC, NULL, (PBYTE)Product, 253, NULL))
							temp.m_desc = Product;
						else
							temp.m_desc = _T("(Unnamed HID device)");
					}
				}
			}

			//if(strstr(detail->DevicePath, vid_str) != NULL
			//	&& ( (pid == 0) || (strstr(detail->DevicePath, pid_str) != NULL) )
			//	)
			//{
			//}

			delete[] (PBYTE)detail;
			if(temp.m_desc != _T("")){
				devs.push_back(temp);
			}
		}
		SetupDiDestroyDeviceInfoList(info);
	}

	return devs.size();
}


const BYTE	Decode[256] = {
   /*  0     1    2    3    4    5    6   7     8   9    A    B     C    D    E   F*/
/*0*/ 240,   1,  88, 143,  17, 147,  22, 216, 184, 161, 104, 217,  12,  11, 194, 211,
/*1*/ 141,  47,  56,  19, 252,  21,  85, 150,  99, 255,  23,  27,  32, 232, 117,  34,
/*2*/ 226, 122, 144,  35,  36,  37,  38,  39, 197,  65, 239, 172, 241,  45, 218, 107,
/*3*/  67,  75, 132,  42,   0, 176,  54,  61, 142, 158,  58,  59, 243, 110,  62, 105,
/*4*/ 135,  55,  70, 224, 201,  90, 134, 220, 165, 115, 200, 157,  76, 213,  78,   6,
/*5*/ 109,  73,  82, 214, 130, 245, 189,  87, 173, 164,  33,  91, 120,  10,   9, 227,
/*6*/  96, 196,   8, 234, 124, 187, 185, 103, 148,  63, 179, 202, 180,   2,  24, 221,
/*7*/ 188, 250,  52,  31,  14, 209, 248,  95, 246,  13,  89, 123, 106, 102, 198,  72,
/*8*/ 192, 129,  68, 126,  97, 222, 159, 210, 136, 133,  41,   3, 163, 166, 208,  53,
/*9*/  98, 114, 146,   5, 156, 149,  18, 118,  46, 154,  51, 207,  64, 171,   4, 121,
/*A*/ 229,  80, 238, 108, 175, 145,  16,  81, 151, 219, 183, 215,  94,  15, 251, 101,
/*B*/  28, 178, 112,  66,  25, 127, 131, 204, 100, 199, 181, 235, 119,  86, 212, 191,
/*C*/ 128, 116,  26, 162,  50,   7,  43,  92,  74,  83,  30,  57,  29, 137, 206, 203,
/*D*/ 138, 231,  93, 190, 168, 155, 139, 160,  48, 167,  49, 195,  79, 152, 113, 223,
/*E*/ 193, 225,  69, 153, 228,  77,  44,  71, 170, 186, 111, 177,  60, 237,  20, 140,
/*F*/ 169, 205, 242, 253, 244, 125, 182, 247,  40, 249, 230, 233, 174,  84, 254, 236,
};
const  BYTE Encode[256] ={
  52,   1, 109, 139, 158, 147,  79, 197,  98,  94,  93,  13,  12, 121, 116, 173,
 166,   4, 150,  19, 238,  21,   6,  26, 110, 180, 194,  27, 176, 204, 202, 115,
  28,  90,  31,  35,  36,  37,  38,  39, 248, 138,  51, 198, 230,  45, 152,  17,
 216, 218, 196, 154, 114, 143,  54,  65,  18, 203,  58,  59, 236,  55,  62, 105,
 156,  41, 179,  48, 130, 226,  66, 231, 127,  81, 200,  49,  76, 229,  78, 220,
 161, 167,  82, 201, 253,  22, 189,  87,   2, 122,  69,  91, 199, 210, 172, 119,
  96, 132, 144,  24, 184, 175, 125, 103,  10,  63, 124,  47, 163,  80,  61, 234,
 178, 222, 145,  73, 193,  30, 151, 188,  92, 159,  33, 123, 100, 245, 131, 181,
 192, 129,  84, 182,  50, 137,  70,  64, 136, 205, 208, 214, 239,  16,  56,   3,
  34, 165, 146,   5, 104, 149,  23, 168, 221, 227, 153, 213, 148,  75,  57, 134,
 215,   9, 195, 140,  89,  72, 141, 217, 212, 240, 232, 157,  43,  88, 252, 164,
  53, 235, 177, 106, 108, 186, 246, 170,   8, 102, 233, 101, 112,  86, 211, 191,
 128, 224,  14, 219,  97,  40, 126, 185,  74,  68, 107, 207, 183, 241, 206, 155,
 142, 117, 135,  15, 190,  77,  83, 171,   7,  11,  46, 169,  71, 111, 133, 223,
  67, 225,  32,  95, 228, 160, 250, 209,  29, 251,  99, 187, 255, 237, 162,  42,
   0,  44, 242,  60, 244,  85, 120, 247, 118, 249, 113, 174,  20, 243, 254,  25,
};
void	memcpy_decode(void* des,const void* src,size_t cnt)
{
	do{
		cnt--;
		((BYTE*)des)[cnt] = Decode[((BYTE*)src)[cnt]];
	}while(cnt);
}
void	memcpy_encode(void* des,const void* src,size_t cnt)
{
	do{
		cnt--;
		((BYTE*)des)[cnt] = Encode[((BYTE*)src)[cnt]];
	}while(cnt);
}

CUsbHidPipe::CUsbHidPipe()
:CUsbHid()
,m_hPipeThread(NULL)
,m_hPipe(NULL)
{
	DWORD id;
	m_hPipeThread = ::CreateThread(NULL,0,PipeProc,this,0,&id);
}

CUsbHidPipe::~CUsbHidPipe()
{
	if(m_hPipe){
		::CloseHandle(m_hPipe);
		m_hPipe = NULL;
	}
	::WaitForSingleObject(m_hPipeThread,1000);
	DWORD code;
	::GetExitCodeThread(m_hPipeThread,&code);
	if(code == STILL_ACTIVE){
		::TerminateThread(m_hPipeThread,0);
	}
	::CloseHandle(m_hPipeThread);
}
DWORD CUsbHidPipe::PipeProc()
{
	BOOL fConnected; 
	LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\MTServerPipe");
	for(;;){
      m_hPipe = CreateNamedPipe( 
          lpszPipename,             // pipe name 
          PIPE_ACCESS_DUPLEX,       // read/write access 
          PIPE_TYPE_MESSAGE |       // message type pipe 
          PIPE_READMODE_MESSAGE |   // message-read mode 
          PIPE_WAIT,                // blocking mode 
          PIPE_UNLIMITED_INSTANCES, // max. instances  
          4096,                  // output buffer size 
          4096,                  // input buffer size 
          0,                        // client time-out 
          NULL);                    // default security attribute 

      if (m_hPipe == INVALID_HANDLE_VALUE){
          //pDlg->m_pDataManager->PostThreadMessage(LS_STATUS,0,
          //  (LPARAM)new CString("Fail to Create Pipe!"));
		  m_hPipe = NULL;
          return 0;
      }
      fConnected = ConnectNamedPipe(m_hPipe, NULL) ? 
         TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); 
      if (fConnected)   {
			const size_t BUF_SIZE = 1024;
			BYTE temp[BUF_SIZE+1]="";
			while (true){
				DWORD cbBytesRead = 0;
				BOOL fSuccess;
				fSuccess = ReadFile(m_hPipe, temp, BUF_SIZE, &cbBytesRead, NULL);
				if (! fSuccess || cbBytesRead == 0)
					break;
				OnHidData(temp,cbBytesRead);
			}
      }else{ 
        // The client could not connect, so close the pipe. 
         CloseHandle(m_hPipe);
		 m_hPipe = NULL;
      }
	}
	return 0;
}
